<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Go 应用代码解析 - 依赖与全局变量</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>1. 依赖与全局变量</h1>
        <nav>
            <a href="index.html">概述</a> |
            <a href="01_setup_dependencies.html" class="active">1. 依赖与全局变量</a> |
            <a href="02_opentelemetry_init.html">2. OpenTelemetry 初始化</a> |
            <a href="03_database_redis_init.html">3. 数据库与 Redis 初始化</a> |
            <a href="04_web_server_routing.html">4. Web 服务器与路由</a> |
            <a href="05_request_handlers.html">5. HTTP 请求处理</a> |
            <a href="06_business_logic.html">6. 核心业务逻辑</a> |
            <a href="07_main_startup_shutdown.html">7. 程序启动与关闭</a>
        </nav>
    </header>

    <main>
        <section id="imports">
            <h2>程序依赖了哪些“工具箱” (Imports)？</h2>
            <p>在 Go 语言中，`import` 关键字就像是告诉程序：“我们要用到这些现成的工具或代码库”。这个程序用到了不少东西，我们分门别类来看看：</p>

            <pre><code class="language-go">import (
	// --- 标准库 ---
	"context"          // 处理请求上下文，尤其在追踪和超时控制中很重要
	"encoding/json"    // 用于将 Go 结构体和 JSON 字符串相互转换 (比如和 Redis 存取数据)
	"errors"           // 创建和处理错误信息
	"fmt"              // 格式化字符串，常用于打印日志或错误信息
	"log"              // 提供基本的日志记录功能
	"math/rand"        // 生成随机数 (比如用于缓存过期时间)
	"net/http"         // 处理 HTTP 请求和响应的基础库
	"os"               // 与操作系统交互，比如读取环境变量、处理信号
	"os/signal"        // 专门用于接收操作系统信号 (比如 Ctrl+C)
	"syscall"          // 更底层的系统调用接口 (这里配合 signal 使用)
	"time"             // 处理时间和日期

	// --- Web 框架 ---
	"github.com/gin-gonic/gin" // 非常流行的 Go Web 框架，简化路由、请求处理等

	// --- 数据库相关 ---
	"gorm.io/driver/mysql" // GORM 框架连接 MySQL 数据库的驱动
	"gorm.io/gorm"         // GORM 框架核心库，简化数据库操作 (ORM)
	"gorm.io/gorm/logger"  // GORM 的日志组件

	// --- Redis 客户端 ---
	"github.com/redis/go-redis/v9"            // Go 连接 Redis 的客户端库
	"github.com/redis/go-redis/extra/redisotel/v9" // 让 Redis 操作能被 OTel 追踪的插件 (关键!)

	// --- 其他第三方库 ---
	"github.com/google/uuid" // 生成全局唯一的 ID (UUID)

	// --- OpenTelemetry 核心 API ---
	"go.opentelemetry.io/otel"                // OTel 的核心 API 包
	"go.opentelemetry.io/otel/attribute"      // 定义追踪信息的属性 (键值对)
	"go.opentelemetry.io/otel/codes"          // 定义追踪状态码 (比如成功/错误)
	"go.opentelemetry.io/otel/trace"          // 定义 Trace 和 Span 的接口

	// --- OpenTelemetry SDK ---
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace" // OTel Exporter: 将追踪数据打印到标准输出 (控制台)
	"go.opentelemetry.io/otel/propagation"     // 处理跨服务追踪信息传递的规则 (比如 W3C Trace Context)
	"go.opentelemetry.io/otel/sdk/resource"    // 定义服务本身的信息 (名字、版本等)
	sdktrace "go.opentelemetry.io/otel/sdk/trace" // OTel SDK 的追踪实现

	// --- OpenTelemetry 语义约定 ---
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0" // 预定义的一些常用属性名 (如 http.method)，避免大家各自取名混乱

	// --- OpenTelemetry 自动插桩 (Contrib & Extra) ---
	"github.com/uptrace/opentelemetry-go-extra/otelgorm" // 让 GORM 操作能被 OTel 自动追踪的插件 (关键!)
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" // 让 Gin 框架处理的 HTTP 请求能被 OTel 自动追踪的中间件 (关键!)
)</code></pre>
            <p><strong>简单总结：</strong></p>
            <ul>
                <li><strong>标准库</strong>: Go 语言自带的基本功能。</li>
                <li><strong>Gin</strong>: 处理网络请求的核心框架。</li>
                <li><strong>GORM & MySQL Driver</strong>: 操作 MySQL 数据库的工具。</li>
                <li><strong>go-redis</strong>: 操作 Redis 缓存的工具。</li>
                <li><strong>OpenTelemetry (OTel)</strong>: 记录程序运行轨迹（追踪）的核心工具及其配套插件。</li>
                <li><strong>UUID</strong>: 用来生成不会重复的用户 ID。</li>
            </ul>
            <p class="highlight">重点关注带 OTel 字样的库，它们是实现分布式追踪的关键。</p>
        </section>

        <section id="globals">
            <h2>全局都能用的“公告板” (Global Variables)</h2>
            <p>程序定义了几个全局变量，意味着在程序的很多地方都可以直接访问和使用它们。</p>
            <pre><code class="language-go">// --- 全局变量 ---
var (
	tracer trace.Tracer // OpenTelemetry 的“追踪器”，用它来开始和结束追踪单元 (Span)
	db     *gorm.DB     // GORM 数据库连接实例，初始化后指向我们的 MySQL 数据库
	rdb    *redis.Client // Redis 客户端连接实例，初始化后指向我们的 Redis 服务
)</code></pre>
            <p><strong>解释：</strong></p>
            <ul>
                <li><code>tracer</code>: 这是我们用来创建 OTel Span 的工具。你可以把它想象成一个能发出“开始记录”和“结束记录”指令的设备。后续我们会看到如何使用它。</li>
                <li><code>db</code>: 一旦数据库连接成功建立，这个变量就会持有这个连接。后续所有需要操作数据库的地方（比如增删改查用户），都会通过这个 `db` 变量来进行。它是一个 `*gorm.DB` 类型，表示是 GORM 库定义的数据库连接对象。</li>
                <li><code>rdb</code>: 类似于 `db`，这个变量持有和 Redis 服务器的连接。所有需要读写 Redis 缓存的操作，都会通过这个 `rdb` 变量进行。它是一个 `*redis.Client` 类型。</li>
            </ul>
            <p>将这三个变量设为全局，主要是为了方便在程序的各个部分（比如不同的请求处理函数、业务逻辑函数）都能共享同一个数据库连接、Redis 连接和 OTel 追踪器实例，避免重复创建。</p>
        </section>

         <section id="data-model">
            <h2>定义用户长什么样 (Data Model)</h2>
            <p>程序需要知道“用户”有哪些信息。这里用一个 Go 的结构体 (struct) 来定义，并且加上了 GORM 需要的“标签” (tag)，告诉 GORM 如何将这个结构体映射到数据库的表中。</p>
            <pre><code class="language-go">// --- GORM 数据模型 ---
type User struct {
	ID        string    `gorm:"type:varchar(100);primaryKey"` // 用户 ID，字符串类型，最大长度 100，是主键
	Name      string    `gorm:"type:varchar(100);not null"`  // 用户名，字符串，最大 100，不能为空
	Email     string    `gorm:"type:varchar(100);unique;not null"` // 邮箱，字符串，最大 100，必须唯一，不能为空
	CreatedAt time.Time // 记录创建时间 (GORM 会自动处理)
	UpdatedAt time.Time // 记录最后更新时间 (GORM 会自动处理)
}</code></pre>
            <p><strong>解释：</strong></p>
            <ul>
                <li><code>type User struct { ... }</code>: 定义了一个名叫 `User` 的数据结构。</li>
                <li><code>ID, Name, Email, CreatedAt, UpdatedAt</code>: 这些是 `User` 结构体的字段，代表用户的不同属性。</li>
                <li><code>`gorm:"..."`</code>: 这部分叫做 struct tag，是给 GORM 看的额外信息。
                    <ul>
                        <li><code>type:varchar(100)</code>: 告诉 GORM 这个字段在 MySQL 数据库里对应 `VARCHAR` 类型，最大长度是 100。</li>
                        <li><code>primaryKey</code>: 表明 `ID` 字段是这张表的主键（唯一标识一条记录）。</li>
                        <li><code>not null</code>: 表明这个字段在数据库里不能为空。</li>
                        <li><code>unique</code>: 表明 `Email` 字段在数据库里必须是唯一的，不能有重复。</li>
                        <li><code>time.Time</code>: GORM 知道如何处理 Go 的 `time.Time` 类型，会自动映射到数据库的日期时间类型，并在创建和更新时自动填充 `CreatedAt` 和 `UpdatedAt`。</li>
                    </ul>
                </li>
            </ul>
            <p>这个 `User` 结构体既用于 Go 程序内部表示用户数据，也指导 GORM 如何在 MySQL 数据库中创建对应的 `users` 表以及如何进行数据读写。</p>
        </section>

        <section id="navigation">
            <h2>下一步</h2>
            <p>了解了程序的家底（依赖和全局变量）和数据结构，接下来我们看看最重要的初始化步骤之一：如何设置 OpenTelemetry 这个“侦探工具”。</p>
            <p>➡️ <a href="02_opentelemetry_init.html">点击这里，学习 OpenTelemetry 的初始化过程</a></p>
        </section>
    </main>

    <footer>
        <p>Go 应用代码解析</p>
    </footer>
</body>
</html>